﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using CCWP7.Model.Parser;
using System.Windows;

namespace CCWP7.Model
{
    public class ServerDataManager : IServerDataManager
    {
        static readonly IsolatedStorageFile _isolatedStorage;
        static readonly DataContractSerializer _serializer;
        static readonly object _lock;

        static ServerDataManager()
        {
            _isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
            _serializer = new DataContractSerializer(typeof(ServerList));
            _lock = new object();
        }

        public ServerDataManager()
        {
            Servers = new Server[0];
        }

        public void Load()
        {
            lock (_lock)
            {
                try
                {
                    if (_isolatedStorage.FileExists(Constants.ServersFileName))
                    {
                        using (var stream = _isolatedStorage.OpenFile(Constants.ServersFileName, System.IO.FileMode.Open))
                        {
                            var serverList = (ServerList)_serializer.ReadObject(stream);

                            Servers = serverList.Servers ?? new Server[0];
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());

                    //MessageBox.Show("Server list has been reset", "Failed to read server list", MessageBoxButton.OK);

                    GC.Collect();
                    GC.WaitForPendingFinalizers();

                    _isolatedStorage.DeleteFile(Constants.ServersFileName);
                }
            }
        }

        public void Save()
        {
            lock (_lock)
            {
                if (_isolatedStorage.FileExists(Constants.ServersFileName))
                    _isolatedStorage.DeleteFile(Constants.ServersFileName);

                using (var stream = _isolatedStorage.OpenFile(Constants.ServersFileName, FileMode.OpenOrCreate))
                {
                    _serializer.WriteObject(stream, new ServerList { Servers = Servers });
                }
            }
        }

        public void Load(IDictionary<string, object> state)
        {
            if (state.ContainsKey(Constants.ServersFileName))
                Servers = (Server[])state[Constants.ServersFileName];
        }

        public void Save(IDictionary<string, object> state)
        {
            if (state != null)
                state[Constants.ServersFileName] = Servers;
        }

        public Server[] Servers { get; private set; }

        public void AddServer(Server server)
        {
            lock (_lock)
            {
                var serverWithSameName = FindServerByName(server.Name);

                if (serverWithSameName != null)
                    throw new ArgumentException("Server with same name already exists");

                Servers = Servers.Union(new[] { server }).ToArray();
            }
        }

        public void RemoveServer(Server serverToRemove)
        {
            lock (_lock)
            {
                var server = FindServerByName(serverToRemove.Name);

                Servers = Servers.Except(Enumerable.Repeat(server, 1)).ToArray();
            }
        }

        Server FindServerByName(string serverName)
        {
            return Servers.FirstOrDefault(s => s.Name == serverName);
        }

        static string GetFileNameForServer(string name)
        {
            return string.Format("{0}.server.xml", name);
        }

        public string GetFullUri(string serverUri)
        {
            if (String.IsNullOrEmpty(serverUri))
                throw new ArgumentException("serverUri is null or empty.", "serverUri");
            if (!Uri.IsWellFormedUriString(serverUri, UriKind.Absolute))
                throw new ArgumentException("serverUri is not well formed.", "serverUri");

            if (serverUri.EndsWith(Constants.XmlServerReportAspx, StringComparison.OrdinalIgnoreCase))
                return serverUri;

            var fullUri = new Uri(new Uri(serverUri, UriKind.Absolute), Constants.XmlServerReportAspx);

            return fullUri.ToString();
        }

        public bool IsServerUriValid(string serverUri)
        {
            if (string.IsNullOrEmpty(serverUri))
                return false;

            if (Uri.IsWellFormedUriString(serverUri, UriKind.Absolute))
                return true;

            return false;
        }

        public bool IsNameValid(string name)
        {
            if (string.IsNullOrEmpty(name))
                return false;

            if (name.Trim() == string.Empty)
                return false;

            return true;
        }

        public void ReadAndUpdateServer(Server server, Action<Server> onCompleted)
        {
            Debug.Assert(onCompleted != null, "onCompleted is null.");
            Debug.Assert(server != null, "server is null.");
            Debug.Assert(!string.IsNullOrEmpty(server.Uri), "server.Uri is not specified.");

            var request = new WebClient();

            request.DownloadStringCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        var report = new XmlParser().Parse(e.Result);

                        server.LastReportRead = DateTime.Now;
                        server.Report = report;
                    }
                    onCompleted(server);
                };
            request.DownloadStringAsync(new Uri(server.Uri));
        }
    }
}
